package ksvc

import (
	"context"
	"fcs-api/src/app/model"
	"time"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/rest"
	"knative.dev/pkg/injection"
	kapiv1 "knative.dev/serving/pkg/apis/serving/v1"
	v1 "knative.dev/serving/pkg/client/informers/externalversions/serving/v1"
	kserviceinformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/service"
)

var serviceInformer v1.ServiceInformer

type Informer struct {
	ServiceInformer v1.ServiceInformer
}

func CreateInformer(cfg *rest.Config, stopCh <-chan struct{}) {
	ctx := context.Background()
	ctx, _ = injection.Default.SetupInformers(ctx, cfg)
	serviceInformer = kserviceinformer.Get(ctx)
	go serviceInformer.Informer().Run(stopCh)
}

func GetInformer() *Informer {
	return &Informer{ServiceInformer: serviceInformer}
}

func (k *Informer) GetKsvc(ns, name string) (*kapiv1.Service, error) {
	return k.ServiceInformer.Lister().Services(ns).Get(name)
}

func (k *Informer) IsSynced() bool {
	return k.ServiceInformer.Informer().HasSynced()
}

func Run(queue chan model.ListenInfo) {
	for listenInfo := range queue {
		li := listenInfo
		// 延迟2秒钟之后执行，防止动作太快ksvc还没建出来
		go time.AfterFunc(time.Second*2, func() {
			// 判断informer是否已经ready
			for {
				if GetInformer().IsSynced() {
					break
				}
				_ = logger.Warn("ksvc informer not ready")
				time.Sleep(time.Second)
			}
			ksvc, err := GetInformer().GetKsvc(li.Namespace, li.Name)
			if err != nil {
				_ = logger.Error("get ksvc ready status error, error is %s, ksvc name is %s, namespace is %s",
					err.Error(), li.Name, li.Namespace)
				if apierrors.IsNotFound(err) {
					li.ReadyChan <- false
					return
				}
			} else {
				if ksvc.IsReady() {
					li.ReadyChan <- true
					return
				}
			}
			// 判断函数是否到达监听的超时时间
			if listenInfo.StartTime.Add(time.Second * time.Duration(model.Base.KsvcWaitingTime)).Before(time.Now()) {
				_ = logger.Warn("CheckFunctionStatus: ksvc not ready due to timeout, ksvc name is %s, namespace is %s",
					li.Name, li.Namespace)
				li.ReadyChan <- false
				return
			}
			// 将还未ready的函数，延迟后重新放入队列中
			time.AfterFunc(10*time.Second, func() {
				queue <- li
			})
		})
	}
}
